/**
 * Game Manager
 */
var GAME_MANAGER = "GameManager";
var GAME_MANAGER_VERSION = "0.5";
var theGameManager = null;

var GAME_STATE = {
	INIT: {id:0, name:"Init"},
	PLAY: {id:1, name:"Play"},
	END : {id:2, name:"End"},
}
var MENU_STATE = {
		NONE    : {id:0, name:"None"},
		MAIN    : {id:1, name:"Main"},
		MESSAGES: {id:2, name:"Messages"},
		VICTORY : {id:3, name:"Victory"},
		DEFEAT  : {id:4, name:"Defeat"},
}

function GameManager() {
	this.id        = "";
	this.state     = GAME_STATE.INIT;
	this.menu      = MENU_STATE.NONE;
	this.round     = 0;
}

function storeGameManager(gm) {
	console.log("StoreGameManager");
	var data = {
		version: GAME_MANAGER_VERSION,
		id:      gm.id,
		state:   gm.state,
		menu:    gm.menu,
		round:   gm.round,
	};
	LocalStorage.set(GAME_MANAGER, JSON.stringify(data));
}
function loadGameManager() {
	var gm = new GameManager();

	var data = JSON.parse(LocalStorage.get(GAME_MANAGER));
	if (data.version != GAME_MANAGER_VERSION) {
		LocalStorage.del(GAME_MANAGER);
		throw "GameManager is wrong version!";
	}
	gm.id    = data.id;
	gm.state = data.state;
	gm.menu  = data.menu;
	gm.round = data.round;

	return gm;
}

function getGameManager() {
	if (!theGameManager) {
		if (LocalStorage.has(GAME_MANAGER)) {
			theGameManager = loadGameManager();
		} else {
			theGameManager = new GameManager();
//			storeGameManager(theGameManager);
		}
	}
	return theGameManager;
}

GameManager.prototype.store = function() {
	console.log("StoreGameManager");
	storeGameManager(this);
}

GameManager.prototype.setMenu = function(menu) {
	this.menu = menu;
	this.onMessage("menu");

	storeGameManager(this);
}
GameManager.prototype.toggleMenu = function(menu) {
	if (this.menu.id == menu.id) {
		this.setMenu(MENU_STATE.NONE);
	} else {
		this.setMenu(menu);
	}
}

GameManager.prototype.getSystemsFromXY = function(x, y) {
	var result = [];

	var systems = getSystemManager().systems;
	for (s in systems) {
		var system = systems[s];
		var dx = system.x - x;
		var dy = system.y - y;
		var r2 = dx*dx + dy*dy;

		result.push({ system: system.id, r2: r2 });
	}

	result.sort(function(a, b) {
		return b.r2 - a.r2;
	});

	return result;
}
GameManager.prototype.getClosestSystems = function(ref) {
	var result = [];
	if (!ref) return result;

	var systems = getSystemManager().systems;
	for (s in systems) {
		var system = systems[s];
		if (system == ref) continue;

		var di = (system.i - ref.i);
		var dj = (system.j - ref.j);
		if (Math.abs(di) + Math.abs(dj) <= 2) {
			result.push(system);
		}
	}

	return result;
}

GameManager.prototype.generateSystems = function(wn, hn, onEven) {
	getSystemManager().clearSystems();

	if (!wn) wn = 9;
	if (!hn) hn = 9;
	var wpx = 320/(wn + 1);
	var hpx = 320/(hn + 1);

	var n = 0;
	for (var j = 0; j < hn; ++j) {
		for (var i = 0; i < wn; ++i) {
			var isEven = (i + j) % 2 == 0;
			if (onEven == isEven) continue;
			++n;
			var x = i * wpx + wpx - 160;
			var y = j * hpx + hpx - 160;

			var id = "s"+ n;
			var player    = null;
			var factories = 1;
			var force     = 0;
			getSystemManager().addSystem({
				id : id,
				i : i,
				j : j,
				x : x,
				y : y,
				player : player,
				force : force,
				factories : factories,
			});
		}
	}
}

GameManager.prototype.initSystems = function() {
	console.log("InitSystems");

	var systems = getSystemManager().getSystems({});
	for (var s in systems) {
		var system = systems[s];
		system.player    = null;
		system.force     = 0;
		system.factories = 1;
		system.target    = null;
	}

//	getPlayerManager().store();
	getSystemManager().store();
//	this.store();
}

GameManager.prototype.initPlayerSystems = function() {
	console.log("InitPlayerSystems");

	var players = getPlayerManager().getPlayers(function(player) {
		return player.type == "player" || player.type == "npc";
	});
	var systems = getSystemManager().getSystems({});
	shuffleArray(systems);
	var playerSystems = [];

	while (players.length > 0 && systems.length > 0) {
		var system = systems.pop();
		var isOk = true;
		for (s in playerSystems) {
			var ps = playerSystems[s];
			var di = ps.i - system.i;
			var dj = ps.j - system.j;
			if (Math.abs(di) + Math.abs(dj) <= 2) isOk = false;
		}
		if (!isOk) continue;

		var player = players.shift();
		player.systems = [system.id];

		system.player    = player.id;
		system.force     = 1;
//		system.factories = 1;
//		system.target    = null;

		playerSystems.push(system);
	}

	getPlayerManager().store();
	getSystemManager().store();
//	this.store();
}

GameManager.prototype.moveShip = function(sourceId, targetId, force) {
	if (!sourceId) return;
	if (!targetId) return;
	if (sourceId == targetId) return;

	var sourceSystem = getSystemManager().getSystem({ id: sourceId });
	var targetSystem = getSystemManager().getSystem({ id: targetId });
	if (!sourceSystem || !targetSystem) return;

	var force = Math.min(sourceSystem.force -1, force);
	if (force <= 0) return false;

	if (sourceSystem.player == targetSystem.player) {
		sourceSystem.force -= force;
		targetSystem.force += force;
	} else {
		sourceSystem.force -= force;
		targetSystem.force -= force;

		if (targetSystem.force == 0) {
			targetSystem.player = null;
			targetSystem.target = null;
		}
		if (targetSystem.force < 0) {
			targetSystem.force *= -1;
			targetSystem.player = sourceSystem.player;
			targetSystem.target = null;
		}
	}

	sourceSystem.uiUpdate();
	targetSystem.uiUpdate();

	getPlayerManager().store();
	getSystemManager().store();
//	storeGameManager(this);
	return true;
}
GameManager.prototype.clearTarget = function(systemId) {
	if (!systemId) return;
	var system = getSystemManager().getSystem({ id: systemId });
	if (!system) return;

	system.target = null;

	if (system.uiTarget) system.uiTarget();
	else console.log("ClearTarget "+ systemId +" !system.uiTarget");

//	getPlayerManager().store();
	getSystemManager().store();

	return true;
}
GameManager.prototype.setTarget = function(systemId, targetId) {
	if (!systemId) return;
	if (!targetId) return;
	if (systemId == targetId) return;

	var system = getSystemManager().getSystem({ id: systemId });
	var target = getSystemManager().getSystem({ id: targetId });
	if (!system || !target) return;

	system.target = targetId;
//	if (this.hasTargetLoop(targetId)) {
//		this.clearTarget(targetId);
//	}

	if (system.uiTarget) system.uiTarget();
	else console.log("SetTarget "+ systemId +" !system.uiTarget");

//	getPlayerManager().store();
	getSystemManager().store();

	return true;
}

GameManager.prototype.hasTargetLoop = function(systemId) {
	var slow = systemId;
	var fast1 = systemId;
	var fast2 = systemId;
	var player = getSystemManager().getSystem({ id: systemId }).player;
	while (slow && fast1 && fast2) {
		var fast1system = getSystemManager().getSystem({ id: fast2 });
		if (!fast1system) return false;
		fast1 = fast1system.target;
		var fast2system = getSystemManager().getSystem({ id: fast1 });
		if (!fast2system) return false;
		fast2 = fast2system.target;
		if (fast1system.player != player || fast2system.player != player) return false;
		if (slow == fast1 || slow == fast2) return true;
		slow = getSystemManager().getSystem({ id: slow }).target;
	}
	return false;
}

GameManager.prototype.newGame = function() {
	this.state = GAME_STATE.INIT;
	this.round = 1;

	getPlayerManager().initPlayers();

	this.generateSystems(7, 7, true);
	this.initSystems();
	this.initPlayerSystems();

	this.state = GAME_STATE.PLAY;
	this.store();
}

GameManager.prototype.endTurn = function() {
	console.log("EndTurn:"+ this.round);

	if (this.state.id == GAME_STATE.PLAY.id) {
		var systems = getSystemManager().getSystems({});

		/*
		 * Build Force
		 */
		for (var s in systems) {
			var system = systems[s];
			if (system.player && !system.target) {
				system.force += system.factories;
				system.uiBuild();
			}
		}

		/*
		 * Move Fleets
		 */
		for (var s in systems) {
			var system = systems[s];
			if (!system.target) continue;

			var target = getSystemManager().getSystem({ id: system.target });
			if (!target) continue;
			if (!target.fleets) target.fleets = [];

			system.target = null;
			var force = system.force-1;
			if (force <= 0) {
				system.uiTarget();
				continue;
			}

			system.force -= force;
			target.fleets.push({ player: system.player, force: force });
			system.uiMove(target.id);
		}

		/*
		 * Resolve Battles
		 */
		for (var s in systems) {
			var system = systems[s];
			if (!system.fleets) continue;

			var fleets = [];
			if (system.player && system.force > 0) {
				fleets.push({ player: system.player, force: system.force });
			}

			for (var f in system.fleets) {
				var fleet = system.fleets[f];
				var exists = false;
				for (var f in fleets) {
					var force = fleets[f];
					if (force.player == fleet.player) {
						force.force += fleet.force;
						exists = true;
					}
				}
				if (!exists) {
					fleets.push({ player: fleet.player, force: fleet.force });
				}
			}

			fleets.sort(function(a, b) { return a.force-b.force; });
			var winner = fleets.pop();

			for (var f in fleets) {
				var fleet = fleets[f];
				winner.force -= fleet.force;

				system.uiBoom();
			}

			if (winner.force <= 0) {
				winner.player = null;
				winner.force = 0;
			}
//			if (system.player != winner.player) {
//				system.target = null;
//			}

			if (system.player != winner.player) {
				if (system.player) getPlayerManager().removeSystem(system.player, system.id);
				system.player = winner.player;
				if (system.player) getPlayerManager().addSystem(system.player, system.id);

				system.uiConquer();
			}
			system.force = winner.force;

			delete system.fleets;
		}

		/*
		 * Increase round
		 */
		++this.round;
	} // state == play

	/*
	 * Update Statistics
	 */

	/*
	 * Signal Winners and Loosers
	 */
	// No competitors
	// 75% systems
	// 75% force
	// ??
	var playersWithSystems = this.getStatus();

	var player = getPlayerManager().getPlayer({ type: "player" });
	if (player.systems.length == 0) {
		this.setMenu(MENU_STATE.DEFEAT);
		this.state = GAME_STATE.END;
	} else if (playersWithSystems == 1) {
		this.setMenu(MENU_STATE.VICTORY);
		this.state = GAME_STATE.END;
	}

	this.store();
	getPlayerManager().store();
	getSystemManager().store();

	return true;
}
GameManager.prototype.getStatus = function() {
	var message = "R:"+ this.round;
	var players = getPlayerManager().getPlayers({});
	var playersWithSystems = 0;
	for (var p in players) {
		var player = players[p];

		if (player.systems.length > 0) {
			var force = 0;
			for (s in player.systems) {
				var system = getSystemManager().getSystem({ id: player.systems[s] });
				force += system.force;
			}

			message += " "+ player.name +":"+ force +"/"+ player.systems.length;
			++playersWithSystems;
		}
	}
	$("#message").html(message);
	return playersWithSystems;
}
GameManager.prototype.setRandomTargets = function() {
	var systems = getSystemManager().getSystems({});
	for (s in systems) {
		var system = systems[s];
		if (system.player == null) continue;

		var player = getPlayerManager().getPlayer({ id: system.player });
		if (player.type != "npc") continue;

		if (system.force > 1 && Math.random() < 0.5) {
			var targets = this.getClosestSystems(system);
			var target = targets[Math.floor(Math.random()*targets.length)];
			system.target = target.id;
		} else {
			system.target = null;
		}
		system.uiTarget();
	}

//	getPlayerManager().store();
	getSystemManager().store();

	return true;
}

GameManager.prototype.listeners = [];
GameManager.prototype.addListener = function(listener) {
	this.listeners.push(listener);
}
GameManager.prototype.onMessage = function(message) {
	for (var l in this.listeners) {
		var listener = this.listeners[l];

		if (typeof listener == "function") {
			listener(message);
		}
		if (typeof listener == "object") {
			if (!listener.onMessage) continue;
			listener.onMessage(message);
		}
	}
}

var TestGameManager = {};

TestGameManager.testMoveShip = function() {
	var gm = new GameManager();

	assertUndefined(gm.moveShip(null, "s1", 1), "testMoveShip 1");
	assertUndefined(gm.moveShip("s0", null, 1), "testMoveShip 2");
	assertUndefined(gm.moveShip("s0", "s1", 0), "testMoveShip 3");
	assertUndefined(gm.moveShip("s0", "s0", 1), "testMoveShip 4");

//	assertTrue(gm.moveShip("s0", "s1", 1), "testMoveShip 5");
}
TestGameManager.testSetTarget = function() {
	var gm = new GameManager();

//	assertTrue(gm.setTarget("s0", "s1"), "testSetTarget 1");
}
TestGameManager.testEndTurn = function() {
	var gm = new GameManager();

//	gm.endTurn();
}

TestGameManager.test = function() {
	try {
		this.testMoveShip();
		this.testSetTarget();
		this.testEndTurn();
	} catch(e) {
		console.error(e);
	}
}
